/* Includes ------------------------------------------------------------------*/
#include "softInterface.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
extern void Delay_us(int n);
extern void Gpio_Output(void *port, uint32_t pin, int level);
extern int Gpio_Input(void *port, uint32_t pin);
extern void Gpio_ModelOutput(void *port, uint32_t pin);
extern void Gpio_ModelInput(void *port, uint32_t pin);
/* Private functions ---------------------------------------------------------*/

/*********************************************************************************************

  * @brief  SoftI2c_Start
  * @param  i2c: i2c object
  * @return 0, successfullu
  *         -1, unkown error
  * @remark send start signal

  ********************************************************************************************/
int SoftI2c_Start(struct SoftI2c *i2c)
{
    uint8_t i = 0;

    //set sda, scl to high
    Gpio_Output(i2c->scl.port, i2c->scl.pin, 1);
    Gpio_Output(i2c->sda.port, i2c->sda.pin, 1);
    I2C_Delay();

    // if sda is not pulled high by slave device, that means the wire is busy
    Gpio_ModelInput(i2c->sda.port, i2c->sda.pin);
    if (!Gpio_Input(i2c->sda.port, i2c->sda.pin))
    {
        // make 9 clock , try to make wire leave lock status
        for (i = 0; i < 9; i++)
        {
            Gpio_Output(i2c->scl.port, i2c->scl.pin, 0);
            I2C_Delay();
            Gpio_Output(i2c->scl.port, i2c->scl.pin, 1);
            I2C_Delay();
        }
        // if sda is still high after 9 clock , the wire is occur unkown error
        if (!Gpio_Input(i2c->sda.port, i2c->sda.pin))
        {   
            Gpio_ModelOutput(i2c->sda.port, i2c->sda.pin);
            return -1;    // EPERM
        }
    }
    Gpio_ModelOutput(i2c->sda.port, i2c->sda.pin);

    Gpio_Output(i2c->sda.port, i2c->sda.pin, 0); // pull down sda
    I2C_Delay();
    Gpio_Output(i2c->scl.port, i2c->scl.pin, 0); // pull down scl after pull down sda to generate start signal
    I2C_Delay();

    return 0;
}

/*********************************************************************************************

  * @brief  SoftI2c_Stop
  * @param  i2c: i2c object
  * @return 
  * @remark send stop signal

  ********************************************************************************************/
void SoftI2c_Stop(struct SoftI2c *i2c)
{
    //拉低SCL,sda
    Gpio_Output(i2c->scl.port, i2c->scl.pin, 0);
    Gpio_Output(i2c->sda.port, i2c->sda.pin, 0);
    I2C_Delay();

    Gpio_Output(i2c->scl.port, i2c->scl.pin, 1); // pull high scl
    I2C_Delay();
    Gpio_Output(i2c->sda.port, i2c->sda.pin, 1); // pull high sda after pull high scl to generate stop signal
    I2C_Delay();
}
/*********************************************************************************************

  * @brief  SoftI2c_Ack
  * @param  i2c: i2c object
  *         isAck, if it is set to true, then send ack
  * @return 
  * @remark send ack signal

  ********************************************************************************************/
void SoftI2c_Ack(struct SoftI2c *i2c, bool isAck)
{
    if (isAck) 
    {   Gpio_Output(i2c->sda.port, i2c->sda.pin, 0);  }
    else
    {   Gpio_Output(i2c->sda.port, i2c->sda.pin, 1);  }
    I2C_Delay();

    Gpio_Output(i2c->scl.port, i2c->scl.pin, 1); 
    I2C_Delay();

    Gpio_Output(i2c->scl.port, i2c->scl.pin, 0);
    I2C_Delay();

    Gpio_Output(i2c->sda.port, i2c->sda.pin, 1); // pull high after ack, slave will control wire
    I2C_Delay();
}

/*********************************************************************************************

  * @brief  SoftI2c_WaitAck
  * @param  i2c: i2c object
  *         isAck, if it is set to true, then send ack
  * @return 0, get the ack
  *         -1, dont get the ack
  * @remark wait for ack

  ********************************************************************************************/
int SoftI2c_WaitAck(struct SoftI2c *i2c)
{
    int Status;
    Gpio_Output(i2c->sda.port, i2c->sda.pin, 1);
    I2C_Delay();
    Gpio_Output(i2c->scl.port, i2c->scl.pin, 1); 
    I2C_Delay();

    Gpio_ModelInput(i2c->sda.port, i2c->sda.pin);
    if (!Gpio_Input(i2c->sda.port, i2c->sda.pin))
    {   Status = 0; }
    else
    {   Status = -1; }
    Gpio_ModelOutput(i2c->sda.port, i2c->sda.pin);

    Gpio_Output(i2c->scl.port, i2c->scl.pin, 0);
    I2C_Delay();
    return Status;
}

/*********************************************************************************************

  * @brief  SoftI2c_SendByte
  * @param  i2c: i2c object
  *         data, the byte which send to slave device
  * @return 
  * @remark 

  ********************************************************************************************/
void SoftI2c_SendByte(struct SoftI2c *i2c, uint8_t data)
{
    uint8_t i = 0;

    for (i = 0; i < 8; i++)
    {
        Gpio_Output(i2c->scl.port, i2c->scl.pin, 0);
        I2C_Delay();

        if (data & (1 << 7))
        {   Gpio_Output(i2c->sda.port, i2c->sda.pin, 1);  }
        else
        {   Gpio_Output(i2c->sda.port, i2c->sda.pin, 0);  }
        I2C_Delay();
        data <<= 1;

        Gpio_Output(i2c->scl.port, i2c->scl.pin, 1);
        I2C_Delay();
    }
    Gpio_Output(i2c->scl.port, i2c->scl.pin, 0);
    I2C_Delay();
}

/*********************************************************************************************

  * @brief  SoftI2c_ReceiveByte
  * @param  i2c: i2c object
  * @return the receive byte from wire
  * @remark 

  ********************************************************************************************/
uint8_t SoftI2c_ReceiveByte(struct SoftI2c *i2c)
{
    uint8_t i = 0, Temp;

    for (i = 0; i < 8; i++)
    {
        Gpio_Output(i2c->scl.port, i2c->scl.pin, 0);
        I2C_Delay();
        Gpio_Output(i2c->scl.port, i2c->scl.pin, 1);
        I2C_Delay();

        Temp <<= 1;
        Gpio_ModelInput(i2c->sda.port, i2c->sda.pin);
        if (Gpio_Input(i2c->sda.port, i2c->sda.pin))
        {   Temp |= 1;  }
        Gpio_ModelOutput(i2c->sda.port, i2c->sda.pin);
        I2C_Delay();
    }

    Gpio_Output(i2c->scl.port, i2c->scl.pin, 0);
    I2C_Delay();

    return Temp;
}
/*********************************************************************************************

  * @brief  SoftI2c_Send
  * @param  i2c: i2c object
  *         data, the data which need send to slave device
  * @return 0, successfully
  *         -1, wire is lock and cant back to normal
  * @remark 

  ********************************************************************************************/
int SoftI2c_Send(struct SoftI2c *i2c, uint8_t data)
{
    uint8_t status = 0;

    // 如果只发低字节的话，则只发一次就结束
    SoftI2c_SendByte(i2c, (uint8_t)data);   

    status = SoftI2c_WaitAck(i2c);
    if (status)
    {
        SoftI2c_Stop(i2c);
        return status;
    }
    
    return 0;
}
/*********************************************************************************************

  * @brief  SoftI2c_Receive
  * @param  i2c: i2c object
  *         isLast, if the data is the last of queue, dont need send ack
  * @return the data which receive from wire
  * @remark 

  ********************************************************************************************/
uint8_t SoftI2c_Receive(struct SoftI2c *i2c, bool isLast)
{
    uint8_t data = SoftI2c_ReceiveByte(i2c);

    if (isLast == true)
    {   SoftI2c_Ack(i2c, 0); }
    else
    {   SoftI2c_Ack(i2c, 1); }

    return data;
}

/*********************************************************************************************

  * @brief  SoftI2c_ExecuteCommand
  * @param  i2c: i2c object
  *         deviceAddr, the 7bit device address
  *         command, 
  * @return -1, no reponse after start signal
  *         -6, no such device or address
  *         -16, the device is busy, cant get ack response
  * @remark 

  ********************************************************************************************/
int SoftI2c_ExecuteCommand(struct SoftI2c *i2c, uint8_t deviceAddr, uint8_t command)
{
    // start signal
    if (SoftI2c_Start(i2c) != 0)
    {   return -1;   }      // EPERM

    // send device address
    if (SoftI2c_Send(i2c, (uint8_t)(deviceAddr << 1)) != 0)
    {   return -6;   }      // ENXIO

    if (SoftI2c_Send(i2c, command) != 0)
    {   return -16;   }     // EBUSY

    // end signal
    SoftI2c_Stop(i2c);

    return 0;
}

/*********************************************************************************************

  * @brief  SoftI2c_SingleWrite
  * @param  i2c: i2c object
  *         deviceAddr, the 7bit device address
  *         command, the first byte to send
  *         data, the second byto to send
  * @return -1, no reponse after start signal
  *         -6, no such device or address
  *         -16, the device is busy, cant get ack response
  * @remark send one byte to i2c slave device

  ********************************************************************************************/
int SoftI2c_SingleWrite(struct SoftI2c *i2c, uint8_t deviceAddr, uint8_t registerAddr, uint8_t data)
{
    // start signal
    if (SoftI2c_Start(i2c) != 0)
    {   return -1;   }    // EPERM

    // send device address
    if (SoftI2c_Send(i2c, (uint8_t)(deviceAddr << 1)) != 0)
    {   return -6;   }    // ENXIO

    if (SoftI2c_Send(i2c, registerAddr) != 0)
    {   return -16;   }   // EBUSY

    if (SoftI2c_Send(i2c, data) != 0)
    {   return -16;   }   // EBUSY

    // end signal
    SoftI2c_Stop(i2c);

    return 0;
}

/*********************************************************************************************

  * @brief  SoftI2c_MultiWrite
  * @param  i2c: i2c object
  *         deviceAddr, the 7bit device address
  *         command, the first byte to send
  *         data, the pointer of byte array
  *         length, the byte count of data 
  * @return -1, no reponse after start signal
  *         -6, no such device or address
  *         -16, the device is busy, cant get ack response
  * @remark send multi byte to i2c slave device

  ********************************************************************************************/
int SoftI2c_MultiWrite(struct SoftI2c *i2c, uint8_t deviceAddr, uint8_t registerAddr, uint8_t *data, size_t length)
{
    uint8_t i = 0;

    // start signal
    if (SoftI2c_Start(i2c) != 0)
    {   return -1;   }    // EPERM

    // send device address
    if (SoftI2c_Send(i2c, (uint8_t)(deviceAddr << 1)) != 0)
    {   return -6;   }    // ENXIO

    if (SoftI2c_Send(i2c, registerAddr) != 0)
    {   return -16;   }   // EBUSY

    for (i = 0; i < length; i++)
    {
        if (SoftI2c_Send(i2c, data[i]) != 0)
        {   return -16;   }   // EBUSY
    }

    // end signal
    SoftI2c_Stop(i2c);

    return 0;
}

/*********************************************************************************************

  * @brief  SoftI2c_MultiWrite
  * @param  i2c: i2c object
  *         deviceAddr, the 7bit device address
  *         address, the address where start read
  *         data, the pointer of byte array
  *         length, the read count
  * @return -1, no reponse after start signal
  *         -6, no such device or address
  *         -16, the device is busy, cant get ack response
  * @remark get multi byte from i2c slave device

  ********************************************************************************************/
int SoftI2c_Read(struct SoftI2c *i2c, uint8_t deviceAddr, uint8_t address, uint8_t *data, size_t len)
{
    // start signal
    if (SoftI2c_Start(i2c) != 0)
    {   return -1;   }    // EPERM

    // send device address
    if (SoftI2c_Send(i2c, (uint8_t)(deviceAddr << 1)) != 0)
    {   return -6;   }    // ENXIO

    if (SoftI2c_Send(i2c, address) != 0)
    {   return -16;   }   // EBUSY

    if (SoftI2c_Start(i2c) != 0)
    {   return -16;   }   // EBUSY

    if (SoftI2c_Send(i2c, (uint8_t)((deviceAddr << 1) | (1 << 0))) != 0)
    {   return -16;   }   // EBUSY

    for (uint8_t i = 0; i < len; i++)
    {
        bool isLast = (i == (len - 1)) ? true : false;
        data[i] = SoftI2c_Receive(i2c, isLast);
    }

    // end signal
    SoftI2c_Stop(i2c);

    return 0;
}
/*********************************************************************************************

  * @brief  SoftSpi_NssControl
  * @param  sspi: spi object
  *         isEnable, set nss pin to low if it is set to true
  * @return 
  * @remark 

  ********************************************************************************************/
void SoftSpi_NssControl(struct SoftSpi *sspi, bool isEnable)
{
    Gpio_Output(sspi->nss.port, sspi->nss.pin, (isEnable)? 0:1);
}


/*********************************************************************************************

  * @brief  SoftSpi_WriteRead
  * @param  sspi: spi object
  *         byte: the byte which send
  * @return the receive byte
  * @remark 

  ********************************************************************************************/
uint8_t SoftSpi_WriteRead(struct SoftSpi *sspi, uint8_t byte)
{
    uint8_t temp8u = 0;
    Gpio_Output(sspi->sck.port, sspi->sck.pin, 0);
  
    for(int8_t i=7; i>=0; i--)
    {
        if((byte & (1<<i)) != 0)
        {   Gpio_Output(sspi->mosi.port, sspi->mosi.pin, 1);  }
        else
        {   Gpio_Output(sspi->mosi.port, sspi->mosi.pin, 0);  }      
        SOFT_SPI_DELAY();
        
        Gpio_Output(sspi->sck.port, sspi->sck.pin, 1);             
        SOFT_SPI_DELAY();
        
        if(Gpio_Input(sspi->miso.port, sspi->miso.pin) == 1)       
        {   temp8u |= (1 << i); }
        
        Gpio_Output(sspi->sck.port, sspi->sck.pin, 0);              
        SOFT_SPI_DELAY();
    }
    
    Gpio_Output(sspi->mosi.port, sspi->mosi.pin, 1);
    
    return temp8u;
}
/*********************************************************************************************

  * @brief  SoftSpi_Write
  * @param  sspi: spi object
  *         data, the pointer of byte array
  *         length, the byte count of data 
  * @return 0: successfully
  * @remark send multi byte to wire

  ********************************************************************************************/
int SoftSpi_Write(struct SoftSpi *sspi, uint8_t *data, size_t length)
{
    for(int i=0; i<length; i++)
    {   SoftSpi_WriteRead(sspi, data[i]); }
    
    return 0;
}  
/*********************************************************************************************

  * @brief  SoftSpi_Write
  * @param  sspi: spi object
  *         data, the pointer of byte array
  *         length, the read count
  * @return 0: successfully
  * @remark get multi byte from wire

  ********************************************************************************************/
int SoftSpi_Read(struct SoftSpi *sspi, uint8_t *data, size_t length)
{
    for(int i=0; i<length; i++)
    {   data[i] = SoftSpi_WriteRead(sspi, 0xFF); }
    
    return 0;
}
